using System;
using System.Collections.Generic;
using Game.Framework.FState;
using Game.Framework.Utils;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.Playables;
using UnityEngine.UI;
using Object = UnityEngine.Object;

namespace Game.Framework.UI
{
    public class UIElement
    {
        public GameObject GameObject => _gameObject;
        public Transform Transform => _transform;

        protected GameObject _gameObject;
        protected Transform _transform;
        public UIElement Parent;

        private static AudioClip _commonBtnClickSound;
        protected Animator _viewAnimator;
        protected PlayableDirector _director;
        protected AudioSource _audioSource;

        private List<UIElement> _subViews;
        private List<UIElement> _pendingToRemoveSubview;


        protected StateMachine<UIStateBase> _stateMachine;

        protected virtual string OpenAnimName => "Appear";
        protected virtual string CloseAnimName => "Disappear";

        private RectTransform _rectTransform;

        public RectTransform RectTrans
        {
            get
            {
                if (_rectTransform == null) _rectTransform = _transform as RectTransform;

                return _rectTransform;
            }
        }

        public bool HasDirector => _director != null;
        public PlayableDirector Director => _director;
        public virtual float OpenTimelineTime => (float)_director.duration;

        public void Create(GameObject obj, UIData data)
        {
            _gameObject = obj;
            _transform = _gameObject.transform;
            _subViews = new();
            _pendingToRemoveSubview = new();
            _viewAnimator = _gameObject.GetComponent<Animator>();
            _director = _gameObject.GetComponent<PlayableDirector>();
            _audioSource = _gameObject.GetComponent<AudioSource>();

            OnCreate();

            OnOpen<UIStateNormal>(data);
        }

        protected virtual void OnCreate()
        {
            _stateMachine = new();

            // if (_audioSource) _audioSource.mute = !Settings.Instance.SoundSwitch;
        }

        protected internal virtual void OnOpen<M, T>(UIData data) where T : UIStateNormal, new() where M : UIStateOpen<T>, new()
        {
            _stateMachine.SetState<M>(new UIStateData(this));
        }

        protected internal virtual void OnOpen<T>(UIData data) where T : UIStateNormal, new()
        {
            OnOpen<UIStateOpen<T>, T>(data);
        }

        protected virtual void OnClose()
        {
            foreach (var uiSubView in _subViews) uiSubView.OnClose();

            _stateMachine.SetState<UIStateClose>(new UIStateData(this));
        }

        protected internal virtual void OnRemove()
        {
            _subViews.Clear();

            Object.Destroy(_gameObject);
        }

        protected Button BindButtonEvent(string path, Action call, bool playCommonSound = true)
        {
            Button btn = null;
            if (string.IsNullOrEmpty(path))
            {
                btn = _gameObject.GetComponent<Button>();
            }
            else
            {
                btn = BindItem<Button>(path);
            }


            if (btn)
            {
                btn.onClick.RemoveAllListeners();
                btn.onClick.AddListener(() =>
                {
                    if (playCommonSound) PlayCommonBtnSound();

                    if (!IsNormalState()) return;

                    call.Invoke();
                });
            }

            return btn;
        }

        protected EventTrigger BindTriggerEvent(string path, UnityAction<BaseEventData> call, EventTriggerType triggerType)
        {
            var trigger = BindItem<EventTrigger>(path);

            if (trigger)
            {
                EventTrigger.Entry entry = new EventTrigger.Entry();
                entry.eventID = triggerType;
                entry.callback.RemoveAllListeners();
                entry.callback = new EventTrigger.TriggerEvent();
                entry.callback.AddListener((value) =>
                {
                    PlayCommonBtnSound();

                    if (!IsNormalState()) return;

                    call.Invoke(value);
                });
                trigger.triggers.Add(entry);
            }

            return trigger;
        }

        protected bool IsNormalState()
        {
            if (!(_stateMachine.CurrentState is UIStateNormal)) return false;
            if (Parent != null && !Parent.IsNormalState()) return false;

            return true;
        }

        protected Button BindButtonEvent(Action call)
        {
            return BindButtonEvent("", call);
        }

        protected Toggle BindToggleEvent(string path, Action<bool> call)
        {
            var toggle = BindItem<Toggle>(path);

            if (toggle)
            {
                toggle.onValueChanged.RemoveAllListeners();
                toggle.onValueChanged.AddListener(on =>
                {
                    if (on) PlayCommonBtnSound();

                    if (!IsNormalState()) return;

                    call.Invoke(on);
                });
            }

            return toggle;
        }

        private void PlayCommonBtnSound()
        {
            // if (_commonBtnClickSound == null) _commonBtnClickSound = ResTool.Instance.Load<AudioClip>(SoundPath.Scm4_common_click);
            // AudioTool.Instance.PlaySound(_commonBtnClickSound);
        }

        protected T BindItem<T>(string path)
        {
            return BindItem<T>(path, _transform);
        }

        protected T BindItem<T>(string path, Transform root)
        {
            if (string.IsNullOrEmpty(path))
                return root.GetComponent<T>();

            return root.Find(path).GetComponent<T>();
        }

        protected Transform BindItem(string path)
        {
            if (string.IsNullOrEmpty(path)) return _transform;

            return _transform.Find(path);
        }


        public T BindElement<T>(string path, UIData data = null) where T : UIElement, new()
        {
            var binditem = BindItem(path);
            if (binditem == null)
            {
                LogUtil.LogError($"BindElement error path:{path}");
                return null;
            }

            return BindElement<T>(binditem.gameObject, data);
        }

        public T AddElement<T>(string filePath, Transform parent, UIData data = null) where T : UIElement, new()
        {
            var prefab = UIManager.Instance.LoadPrefab(filePath);
            if (!prefab)
            {
                LogUtil.LogError($"SubView Load Failed:{filePath}");
                return null;
            }

            var obj = Object.Instantiate(prefab);
            return AddElement<T>(obj, parent, data);
        }

        public T AddElement<T>(GameObject obj, Transform parent = null, UIData data = null) where T : UIElement, new()
        {
            if (parent)
            {
                obj.transform.SetParent(parent, false);
            }

            return BindElement<T>(obj, data);
        }

        protected void RemoveSubview(UIElement view)
        {
            _pendingToRemoveSubview.Add(view);
        }

        public void RemoveFromParent()
        {
            Parent.RemoveSubview(this);
        }

        public T BindElement<T>(GameObject obj, UIData data = null) where T : UIElement, new()
        {
            var subView = new T();
            subView.Parent = this;
            subView.Create(obj, data);

            _subViews.Add(subView);

            return subView;
        }

        internal float PlayOpenAnim()
        {
            return PlayAnim(OpenAnimName);
        }

        internal float PlayCloseAnim()
        {
            var time = PlayAnim(CloseAnimName);

            var parentTime = Parent?.GetCloseTime() ?? 0;

            return Mathf.Max(time, parentTime);
        }

        internal float GetCloseTime()
        {
            if (Util.HasAnim(_viewAnimator, CloseAnimName))
            {
                return _viewAnimator ? Util.GetAnimTime(_viewAnimator, CloseAnimName) : (Parent == null ? 0 : Parent.GetCloseTime());
            }

            return (Parent == null ? 0 : Parent.GetCloseTime());
        }

        public float PlayAnim(string animName)
        {
            if (!_viewAnimator) return 0f;
            _viewAnimator.Play(animName);

            return Util.GetAnimTime(_viewAnimator, animName);
        }

        public float GetAnimTime(string animName)
        {
            if (!_viewAnimator) return 0f;

            return Util.GetAnimTime(_viewAnimator, animName);
        }

        public float DirectorPlay(string fileName)
        {
            if (!_director) return 0f;

            var hander = YooAssetManager.Instance.Package.LoadAssetSync<GameObject>(fileName); 

            var timelineAsset = hander.AssetObject as PlayableAsset;
            _director.Play(timelineAsset);

            return (float)timelineAsset.duration;
        }

        protected internal void FixedUpdate(float deltaTime)
        {
            var count = _subViews.Count;
            for (var index = 0; index < count; index++)
            {
                if (index < _subViews.Count)
                {
                    var uiElement = _subViews[index];
                    uiElement.FixedUpdate(deltaTime);
                }
            }

            ClearSubViews();

            _stateMachine.FixedUpdate(deltaTime);
        }

        public virtual void OnOpenFinish()
        {
        }

        private void ClearSubViews()
        {
            foreach (var sub in _pendingToRemoveSubview)
            {
                sub.OnClose();
                sub.OnRemove();
                _subViews.Remove(sub);
            }

            _pendingToRemoveSubview.Clear();
        }

        public void ChangeState<T>(StateData param) where T : UIStateBase, new()
        {
            _stateMachine.SetState<T>(param);
        }
    }
}